/*
 ------------------------------------------------------------------------------
 Copyright (C) 2013 Eternal Games.

 This file is part of the GLQuake source code.

 The GLQuake source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The GLQuake source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the GLQuake source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// cg_weapon.cpp - ...
//


#include "cg_local.h"


struct weapon_t {
	bool				registered;

	model_t *			model;
	soundShader_t *		fireSound;
};

static weapon_t			cg_weaponTypes[WP_MAX_TYPES];


/*
 ==============================================================================

 LOADING

 ==============================================================================
*/


/*
 ==================
 CG_LoadAxe
 ==================
*/
static void CG_LoadAxe (weapon_t *weapon){

	weapon->model = glqRenderer->RegisterModel("progs/v_axe.mdl");
	weapon->fireSound = glqSound->RegisterSoundShader("weapons/axe");
}

/*
 ==================
 CG_LoadShotgun
 ==================
*/
static void CG_LoadShotgun (weapon_t *weapon){

	weapon->model = glqRenderer->RegisterModel("progs/v_shot.mdl");
	weapon->fireSound = glqSound->RegisterSoundShader("weapons/shotgun");
}

/*
 ==================
 CG_LoadSuperShotgun
 ==================
*/
static void CG_LoadSuperShotgun (weapon_t *weapon){

	weapon->model = glqRenderer->RegisterModel("progs/v_shot2.mdl");
	weapon->fireSound = glqSound->RegisterSoundShader("weapons/superShotgun");
}

/*
 ==================
 CG_LoadNailgun
 ==================
*/
static void CG_LoadNailgun (weapon_t *weapon){

	weapon->model = glqRenderer->RegisterModel("progs/v_nail.mdl");
	weapon->fireSound = glqSound->RegisterSoundShader("weapons/nailgun");
}

/*
 ==================
 CG_LoadSuperNailgun
 ==================
*/
static void CG_LoadSuperNailgun (weapon_t *weapon){

	weapon->model = glqRenderer->RegisterModel("progs/v_nail2.mdl");
	weapon->fireSound = glqSound->RegisterSoundShader("weapons/superNailgun");
}

/*
 ==================
 CG_LoadGrenadeLauncher
 ==================
*/
static void CG_LoadGrenadeLauncher (weapon_t *weapon){

	weapon->model = glqRenderer->RegisterModel("progs/v_rock.mdl");
	weapon->fireSound = glqSound->RegisterSoundShader("weapons/grenadeLauncher");
}

/*
 ==================
 CG_LoadRocketLauncher
 ==================
*/
static void CG_LoadRocketLauncher (weapon_t *weapon){

	weapon->model = glqRenderer->RegisterModel("progs/v_rock2.mdl");
	weapon->fireSound = glqSound->RegisterSoundShader("weapons/rocketLauncher");
}

/*
 ==================
 CG_LoadLightninggun
 ==================
*/
static void CG_LoadLightninggun (weapon_t *weapon){

	weapon->model = glqRenderer->RegisterModel("progs/v_light.mdl");
	weapon->fireSound = glqSound->RegisterSoundShader("weapons/lightninggun");
}

/*
 ==================
 
 ==================
*/
static weapon_t *CG_LoadWeapon (int type){

	weapon_t	*weapon;

	if (type < 0 || type >= WP_MAX_TYPES)
		G_Error("CG_RegisterWeaponType: type out of range");

	weapon = &cg_weaponTypes[type];

	if (weapon->registered)
		return weapon;

	// Fill it in
	weapon->registered = true;

	switch (type){
	case WP_NONE:

		break;
	case WP_AXE:
		CG_LoadAxe(weapon);
		break;
	case WP_SHOTGUN:
		CG_LoadShotgun(weapon);
		break;
	case WP_SUPERSHOTGUN:
		CG_LoadSuperShotgun(weapon);
		break;
	case WP_NAILGUN:
		CG_LoadNailgun(weapon);
		break;
	case WP_SUPERNAILGUN:
		CG_LoadSuperNailgun(weapon);
		break;
	case WP_GRENADELAUNCHER:
		CG_LoadGrenadeLauncher(weapon);
		break;
	case WP_ROCKETLAUNCHER:
		CG_LoadRocketLauncher(weapon);
		break;
	case WP_LIGHTNINGUN:
		CG_LoadLightninggun(weapon);
		break;
	default:
		G_Error("CG_RegisterWeaponType: bad weapon type");
	}

	// Add to hash table

	return weapon;
}

/*
 ==================
 
 ==================
*/
static weapon_t *CG_FindWeapon (int type){
	
	weapon_t	*weapon;

	// See if already loaded

	// Load it
	weapon = CG_LoadWeapon(type);

	return weapon;
}

/*
 ==================
 CG_RegisterWeapon
 ==================
*/
static weapon_t *CG_RegisterWeapon (int type){

	return CG_FindWeapon(type);
}


/*
 ==============================================================================

 RENDERING

 ==============================================================================
*/


/*
 ==================

 ==================
*/
static void CG_UpdateWeaponEntity (weapon_t *weapon, playerState_t *playerState, entity_t *entity, glqVec3 &origin){

	glqMemory->Fill(&entity->renderEntity, 0, sizeof(renderEntity_t));

	// Set up the render entity
	entity->renderEntity.type = RE_MODEL;
	entity->renderEntity.entityNum = ENTITYNUM_NONE;

	entity->renderEntity.origin = origin;
	entity->renderEntity.axis = cg.renderView.axis;

	entity->renderEntity.model = weapon->model;

	entity->renderEntity.skin = NULL;

	entity->renderEntity.frame = playerState->weaponFrame;
	entity->renderEntity.oldFrame = playerState->weaponFrame;	// FIXME: needs the old playerState

	entity->renderEntity.backLerp = 1.0f - cg.frameInterpolation;

	entity->renderEntity.depthHack = true;

	entity->renderEntity.allowInView = VIEW_ALL;
	entity->renderEntity.allowShadowInView = VIEW_ALL;

	entity->renderEntity.material = NULL;

	entity->renderEntity.materialParms[MATERIALPARM_RED] = 1.0f;
	entity->renderEntity.materialParms[MATERIALPARM_GREEN] = 1.0f;
	entity->renderEntity.materialParms[MATERIALPARM_BLUE] = 1.0f;
	entity->renderEntity.materialParms[MATERIALPARM_ALPHA] = 1.0f;
	entity->renderEntity.materialParms[MATERIALPARM_TIMEOFFSET] = -MS2SEC(cg.time);
	entity->renderEntity.materialParms[MATERIALPARM_DIVERSITY] = game.random.RandFloat();
	entity->renderEntity.materialParms[MATERIALPARM_MISC] = 0.0f;
	entity->renderEntity.materialParms[MATERIALPARM_MODE] = 0.0f;

	entity->renderEntity.soundEmitterHandle = 0;

	// Add or update the render entity
	if (!entity->entityHandle)
		entity->entityHandle = glqRenderer->AddRenderEntity(cgs.scene, &entity->renderEntity);
	else
		glqRenderer->UpdateRenderEntity(cgs.scene, entity->entityHandle, &entity->renderEntity);
}

/*
 ==================

 TODO: add bobing and turning ofs
 ==================
*/
static void CG_CalcWeaponPosition (glqVec3 &origin){

	// Calculate the position
	origin = cg.renderView.origin;

	origin += cg.renderView.axis[0] * cg_gunX->floatValue;
	origin += cg.renderView.axis[1] * cg_gunY->floatValue;
	origin += cg.renderView.axis[2] * cg_gunZ->floatValue;

	// Calculate the angles

	// Calculate turning offsets

	// Drop the weapon when landing after a jump or fall
}

/*
 ==================

 ==================
*/
void CG_AddViewWeapon (playerState_t *playerState, entity_t *entity){

	weapon_t	*weapon;
	glqVec3		origin;

	if (!cg_drawGun->integerValue || cg_hand->integerValue == 1)
		return;

	if (cg_fov->floatValue > 90.0f)
		return;

	if (cg.testModel.isGun)
		return;

	// Register the weapon
	weapon = CG_RegisterWeapon(playerState->weaponType);

	// Set up the weapon position and angles
	CG_CalcWeaponPosition(origin);

	// Update the weapon entity
	CG_UpdateWeaponEntity(weapon, playerState, entity, origin);
}


/*
 ==============================================================================

 EVENTS

 ==============================================================================
*/


/*
 ==================

 TODO: where do we set up the sound emitter?
 ==================
*/
void CG_FireWeaponSound (entity_t *entity){

	weapon_t	*weapon;
	soundEmitter_t	soundEmitter;

	weapon = CG_FindWeapon(entity->state->weaponType);
	if (!weapon)
		return;

	// Set up the sound emitter
	soundEmitter.emitterId = ENTITYNUM_NONE;

	soundEmitter.origin = cg.renderView.origin + cg.renderView.axis[0] * 100.0f;

	if (glqCmdSystem->Argc() == 2)
		soundEmitter.direction.Clear();
	else {
		soundEmitter.direction[0] = Str_ToFloat(glqCmdSystem->Argv(2));
		soundEmitter.direction[1] = Str_ToFloat(glqCmdSystem->Argv(3));
		soundEmitter.direction[2] = Str_ToFloat(glqCmdSystem->Argv(4));

		soundEmitter.direction.Normalize();
	}

	soundEmitter.underwater = false;

	soundEmitter.velocity.Clear();

	soundEmitter.soundParms[SOUNDPARM_VOLUME] = 1.0f;
	soundEmitter.soundParms[SOUNDPARM_PITCH] = 1.0f;
	soundEmitter.soundParms[SOUNDPARM_DRYFILTER] = 1.0f;
	soundEmitter.soundParms[SOUNDPARM_DRYFILTERHF] = 1.0f;
	soundEmitter.soundParms[SOUNDPARM_WETFILTER] = 1.0f;
	soundEmitter.soundParms[SOUNDPARM_WETFILTERHF] = 1.0f;
	soundEmitter.soundParms[SOUNDPARM_MINDISTANCE] = 0.0f;
	soundEmitter.soundParms[SOUNDPARM_MAXDISTANCE] = 0.0f;

	// Add or update the sound emitter
	if (!entity->emitterHandle)
		entity->emitterHandle = glqSound->AddSoundEmitter(&soundEmitter);
	else
		glqSound->UpdateSoundEmitter(entity->emitterHandle, &soundEmitter);

	if (!entity->emitterHandle)
		return;

	glqSound->PlaySoundFromEmitter(entity->emitterHandle, SND_CHANNEL_WEAPON, cg.time, weapon->fireSound);
}